<template>
  <div class="hello">
    <div id="container" style="width: 100vw; height: 100vh"></div>
    <el-dialog
      :title="isEdit ? '编辑' : '新增'"
      :visible.sync="dialogVisible"
      width="30%"
    >
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="姓名" prop="xm">
          <el-input v-model="form.xm"></el-input>
        </el-form-item>
        <el-form-item label="所属组织">
          <el-input disabled v-model="form.zz"></el-input>
        </el-form-item>
        <el-form-item label="职级">
          <el-input disabled v-model="form.zj"></el-input>
        </el-form-item>
        <el-form-item label="职务">
          <el-input disabled v-model="form.zw"></el-input>
        </el-form-item>
        <el-form-item label="岗位">
          <el-input disabled v-model="form.gw"></el-input>
        </el-form-item>
        <el-form-item label="位置层级" prop="cj">
          <el-input-number v-model="form.cj" :step="0.5"></el-input-number>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="handleSubmit('form')"
          >确 定</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>

<script>
import { Graph, Path } from "@antv/x6";
import Hierarchy from "@antv/hierarchy";
import plusIcon from "../assets/plus.png";
import minusIcon from "../assets/minus.png";
import editIcon from "../assets/edit.png";
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  data() {
    return {
      graphData: {
        // 默认的节点数据
        shape: "top", // 指定使用何种图形，默认值为 'rect'
        id: "node1",
        x: this.graphMiddleWidth,
        y: 0,
        width: 100,
        height: 40,
        label: "第一级节点", // String，节点标签
        editForm: {
          xm: "第一级节点",
          zz: "总部机关",
          zj: "副总师级别",
          zw: "主任",
          gw: "||||||||",
          cj: 1,
        },
        children: [],
      },
      graphContainer: null,
      graphMiddleWidth: 0, // 画布宽度的中间位置
      dialogVisible: false,
      form: {
        xm: "",
        zz: "总部机关",
        zj: "副总师级别",
        zw: "主任",
        gw: "||||||||",
        cj: "",
      },
      rules: {
        xm: [{ required: true, message: "请输入姓名", trigger: "blur" }],
        cj: [{ required: true, message: "请输入位置层级", trigger: "blur" }],
      },
      parentNode: {},
      isEdit: false,
    };
  },
  beforeCreate() {
    // 第一级节点
    Graph.registerNode(
      "top",
      {
        inherit: "rect",
        markup: [
          {
            tagName: "rect",
            selector: "body",
          },
          {
            tagName: "image",
            selector: "img-add",
          },
          {
            tagName: "image",
            selector: "img-edit",
          },
          {
            tagName: "text",
            selector: "label",
          },
        ],
        attrs: {
          body: {
            rx: 6,
            ry: 6,
            stroke: "#5F95FF",
            fill: "#EFF4FF",
            strokeWidth: 1,
          },
          "img-add": {
            ref: "body",
            refX: "0%",
            refY: "100%",
            refY2: 4,
            width: 16,
            height: 16,
            "xlink:href": plusIcon,
            event: "node:add",
            cursor: "pointer",
          },
          "img-edit": {
            ref: "body",
            refX: "80%",
            refY: "100%",
            refY2: 4,
            width: 16,
            height: 16,
            "xlink:href": editIcon,
            event: "node:edit",
            cursor: "pointer",
          },
          label: {
            fontSize: 14,
            fill: "#262626",
          },
        },
      },
      true
    );
    // 第一级以下的节点
    Graph.registerNode(
      "topic",
      {
        inherit: "rect",
        markup: [
          {
            tagName: "rect",
            selector: "body",
          },
          {
            tagName: "image",
            selector: "img-add",
          },
          {
            tagName: "image",
            selector: "img-remove",
          },
          {
            tagName: "image",
            selector: "img-edit",
          },
          {
            tagName: "text",
            selector: "label",
          },
        ],
        attrs: {
          body: {
            rx: 6,
            ry: 6,
            stroke: "#5F95FF",
            fill: "#EFF4FF",
            strokeWidth: 1,
          },
          "img-add": {
            ref: "body",
            refX: "0%",
            refY: "100%",
            refY2: 4,
            width: 16,
            height: 16,
            "xlink:href": plusIcon,
            event: "node:add",
            cursor: "pointer",
          },
          "img-remove": {
            ref: "body",
            refX: "25%",
            refY: "100%",
            refY2: 4,
            width: 16,
            height: 16,
            "xlink:href": minusIcon,
            event: "node:remove",
            cursor: "pointer",
          },
          "img-edit": {
            ref: "body",
            refX: "80%",
            refY: "100%",
            refY2: 4,
            width: 16,
            height: 16,
            "xlink:href": editIcon,
            event: "node:edit",
            cursor: "pointer",
          },
          label: {
            fontSize: 14,
            fill: "#262626",
          },
        },
      },
      true
    );
    // 连接器
    Graph.registerConnector(
      "mindmap",
      (sourcePoint, targetPoint, routerPoints, options) => {
        const midX = sourcePoint.x;
        const midY = sourcePoint.y;
        const ctrX = targetPoint.x;
        const ctrY = (targetPoint.y - midY) / 5 + midY;
        const pathData = `
       M ${sourcePoint.x} ${sourcePoint.y}
       L ${midX} ${midY}
       Q ${ctrX} ${ctrY} ${targetPoint.x} ${targetPoint.y}
      `;
        return options.raw ? Path.parse(pathData) : pathData;
      },
      true
    );
    // 边 - 连接线
    Graph.registerEdge(
      "mindmap-edge",
      {
        inherit: "edge",
        connector: {
          name: "mindmap",
        },
        attrs: {
          line: {
            targetMarker: "",
            stroke: "#A2B1C3", // 线的颜色
            strokeWidth: 2, // 连接线的粗细
          },
        },
        zIndex: 0,
      },
      true
    );
  },
  mounted() {
    const el = document.getElementById("container");
    this.graphMiddleWidth = (el.clientWidth - 100) / 2;
    this.$nextTick(() => {
      this.graphContainer = new Graph({
        container: el,
        // width: 800,   // 画布宽度
        // height: 600, // 画布高度 - 可以使用dom元素的宽高
        panning: true, // 禁止画布的拖拽
        background: {
          color: "#fffbe6", // 设置画布背景颜色
        },
        grid: {
          size: 10, // 网格大小 10px
          visible: true, // 渲染网格背景
        },
        interacting: false, // 禁止边和节点的交互   https://antv-x6.gitee.io/zh/docs/api/graph/interaction#interacting
      });
      // 监听添加事件
      this.graphContainer.on("node:add", ({ node }) => {
        this.dialogVisible = true;
        this.isEdit = false;
        this.parentNode = node;
      });
      // 监听删除事件
      this.graphContainer.on("node:remove", ({ node }) => {
        const { id } = node;
        this.removeItem(this.graphData, id);
        this.render();
      });
      // 监听编辑事件
      this.graphContainer.on("node:edit", ({ node }) => {
        this.isEdit = true;
        const { id } = node;
        this.form = this.getEditData(id);
        this.parentNode = node;
        this.dialogVisible = true;
      });
      this.graphData.x = this.graphMiddleWidth;
      const data = { nodes: [this.graphData] };
      this.graphContainer.fromJSON(data);
      this.graphContainer.centerContent();
    });
  },
  methods: {
    // 根据id删除节点
    removeItem(obj, id) {
      const { children } = obj;
      if (children) {
        for (let i = 0; i < children.length; i++) {
          const element = children[i];
          if (element.id === id) {
            // TODO：记得问问需不需要这逻辑
            // if (element.children && element.children.length) {
            //   this.$message.error("请先删除下级元素");
            // } else {
            //   children.splice(i);
            // }
            children.splice(i);
          } else {
            this.removeItem(element, id);
          }
        }
      }
    },
    // 重新生成画布内元素
    render() {
      const result = Hierarchy.compactBox(this.graphData, {
        direction: "TB",
        // nodeSep: 150,
        getHeight(d) {
          return d.height;
        },
        getWidth(d) {
          return d.width;
        },
        getHGap() {
          return 40;
        },
        getVGap() {
          return 40;
        },
      });
      const cells = [];
      const traverse = (hierarchyItem) => {
        if (hierarchyItem) {
          const { data, children } = hierarchyItem;
          console.log("data.y: ", data.y);
          console.log("hierarchyItem.y: ", hierarchyItem.y);
          cells.push(
            this.graphContainer.createNode({
              id: data.id,
              shape: data.shape,
              x: hierarchyItem.x,
              y: hierarchyItem.y - data.y,
              width: data.width,
              height: data.height,
              label: data.label,
              ports:
                data.id === "node1"
                  ? false
                  : {
                      groups: {
                        top: {
                          position: "top",
                          attrs: {
                            circle: {
                              magnet: true,
                              stroke: "#5F95FF",
                              strokeWidth: 1,
                              r: 4,
                            },
                          },
                        },
                      },
                      items: [
                        {
                          id: data.id,
                          group: "top",
                          tip: "port-2-tip",
                        },
                      ],
                    },
            })
          );
          if (children) {
            children.forEach((item) => {
              const { id, data } = item;
              cells.push(
                this.graphContainer.createEdge({
                  shape: "mindmap-edge",
                  source: {
                    cell: hierarchyItem.id,
                    anchor: {
                      name: "bottom",
                      args: {
                        dx: 0,
                        dy: 15,
                      },
                    },
                  },
                  target: {
                    cell: id,
                    port: id,
                    anchor: {
                      name: "center",
                      args: {
                        dy: -4,
                      },
                    },
                  },
                })
              );
              traverse(item);
            });
          }
        }
      };
      traverse(result);
      this.graphContainer.resetCells(cells);
      this.graphContainer.centerContent();
      this.dialogVisible = false;
    },
    // 查找对应的id节点
    findItem(obj, id) {
      if (obj.id === id) {
        return {
          parent: null,
          node: obj,
        };
      }
      const { children } = obj;
      if (children) {
        for (let i = 0, len = children.length; i < len; i++) {
          const res = this.findItem(children[i], id);
          if (res) {
            return {
              parent: res.parent || obj,
              node: res.node,
            };
          }
        }
      }
      return null;
    },
    // 添加节点
    addChildNode(id) {
      const res = this.findItem(this.graphData, id);
      const dataItem = res?.node;
      if (dataItem) {
        let item = null;
        const length = dataItem.children ? dataItem.children.length : 0;
        item = {
          id: `${id}-${length + 1}`,
          shape: "topic",
          label: this.form.xm,
          editForm: this.form, // 用于编辑时回显弹窗表单数据 - 需要在初始化第一级节点是加上该字段
          y: String(this.form.cj).indexOf(".") !== -1 ? 20 : 0,
          width: 100,
          height: 40,
        };
        if (item) {
          if (dataItem.children) {
            dataItem.children.push(item);
          } else {
            dataItem.children = [item];
          }
          return item;
        }
      }
      return null;
    },
    // 获取编辑节点数据
    getEditData(id) {
      const res = this.findItem(this.graphData, id);
      const dataItem = res?.node;
      if (dataItem) {
        return dataItem.editForm;
      }
      return null;
    },
    // 编辑节点
    editChildNode(id) {
      const res = this.findItem(this.graphData, id);
      const dataItem = res?.node;
      if (dataItem) {
        dataItem.editForm = this.form;
        dataItem.label = this.form.xm;
        dataItem.y = String(this.form.cj).indexOf(".") !== -1 ? 20 : 0;
      }
      return dataItem;
    },
    // 弹窗的保存方法
    handleSubmit(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          const node = this.parentNode;
          const { id } = node;
          if (this.isEdit) {
            if (this.editChildNode(id)) {
              this.render();
            }
          } else {
            if (this.addChildNode(id)) {
              this.render();
            }
          }
        }
      });
    },
  },
};
</script>
